package come.tool.map;

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;

import org.come.model.Door;

public class MapDecoder {

    private int width;

    private int height;

    private int[][] segmentsOffset;

    private Object[][] jpegDatas;

    private String filename;

    private MyRandomAccessFile mapFile;

    private int horSegmentCount;

    private int verSegmentCount;

    public boolean isNew;

    public MaskHead maskhead;

    public MaskUnit[] maskunit;

    //记录加载的图像数
    public MyRandomAccessFile getMapFile() {
        return mapFile;
    }

    public void setMapFile(MyRandomAccessFile mapFile) {
        this.mapFile = mapFile;
    }

    public MapDecoder(String filename) throws Exception {
        this(new File(filename));
    }

    public MapDecoder(File file) throws Exception {
        this.filename = file.getName();
        mapFile = new MyRandomAccessFile(file, "r");
    }

    /**
     * 从流加载MAP
     *
     * @param
     */
    public byte[][] loadHeader(List<Door> doors) {
        byte[][] bs = null;
        if (!isValidMapFile()) {
            throw new IllegalArgumentException("非梦幻地图格式文件!");
        }
        try {
            // start decoding
            width = mapFile.readInt2();
            height = mapFile.readInt2();
            horSegmentCount = (int) Math.ceil(width / 320.0);
            verSegmentCount = (int) Math.ceil(height / 240.0);
            segmentsOffset = new int[horSegmentCount][verSegmentCount];
            jpegDatas = new Object[horSegmentCount][verSegmentCount];
            for (int v = 0; v < verSegmentCount; v++) {
                for (int h = 0; h < horSegmentCount; h++) {
                    segmentsOffset[h][v] = mapFile.readInt2();
                }
            }

            // 读取地图掩码
            if (isNew) {
                maskhead = new MaskHead();
                maskhead.unknown = mapFile.readInt2();    // 无用
                maskhead.maskNum = mapFile.readInt2();

                maskhead.maskList = new int[maskhead.maskNum];
                maskunit = new MaskUnit[maskhead.maskNum];

                for (int i = 0; i < maskhead.maskNum; i++) {
                    maskhead.maskList[i] = mapFile.readInt2();
                }

                // 读取所有mask单元数据
                for (int i = 0; i < maskhead.maskNum; i++) {
                    ReadMaskUint(i);
                }

            }

            bs = new byte[verSegmentCount * 20][horSegmentCount * 20];
            for (int v = 0; v < verSegmentCount; v++) {
                for (int h = 0; h < horSegmentCount; h++) {
                    getJpegData(h, v, bs);
                }
            }

            // 把每个遮挡块的数据转换成具体的遮挡坐标存放在遮挡数组中
            if (isNew) {
                for (MaskUnit mu : maskunit) {
                    mu.getMsk(bs);
                }
            }
            // 把所有传送矩形放入地图规则中
            /**
             for (Door door : doors) {
             /	door.getDoor(bs);
             }
             */


        } catch (Exception e) {
            throw new IllegalArgumentException("地图解码失败:" + e.getMessage());
        }
        return bs;
    }


    /// <summary>
    /// 读取mask的数据
    /// </summary>
    /// <param name="index"></param>
    private void ReadMaskUint(int index) throws IOException {
        int startaddress = (int) maskhead.maskList[index];
        maskunit[index] = new MaskUnit();
        mapFile.seek(startaddress);
        maskunit[index].startX = mapFile.readInt2();
        maskunit[index].startY = mapFile.readInt2();
        maskunit[index].width = mapFile.readInt2();
        maskunit[index].height = mapFile.readInt2();
        maskunit[index].maskN = mapFile.readInt2();
        maskunit[index].maskData = new byte[maskunit[index].maskN];
    }


    /**
     * 读取地图规则
     * LECC
     * h表示第h个width  v表示第v个height
     */
    public void cell(int h, int v, byte[][] bs, byte[] ccles) {
        //表示换行次数
        int gao = 0;
        for (int i = 0; i < ccles.length; i++) {
            bs[v * 12 + gao][h * 16 + i % 16] = ccles[i];
            if (i % 16 == 15) {
                gao++;
            }
        }
    }

    /**
     * 读取地图规则（新地图）
     * LECC
     * h表示第h个width  v表示第v个height
     */
    public void cell2(int h, int v, byte[][] bs, byte[] ccles) {
        //表示换行次数
        int gao = 0;
        for (int i = 0; i < ccles.length; i++) {
            bs[v * 12 + gao][h * 16 + i % 16] = (byte) (ccles[i] == 1 ? 0 : 1);
            if (i % 16 == 15) {
                gao++;
            }
        }
    }

    /**
     * 获取指定的JPEG数据块和地图规则
     *
     * @param h 行
     * @param v 列
     * @return
     */
    public void getJpegData(int h, int v, byte[][] bs) {
        try {
            int len = 0;
            byte jpegBuf[] = null;
            byte jpegBuf1[] = null;
            mapFile.seek(segmentsOffset[h][v]);// XXX offset
            if (isNew) {
                if (isJPEGData2()) {
                    len = mapFile.readInt2();
                    jpegBuf = new byte[len];
                    mapFile.readFully(jpegBuf);
                    jpegDatas[h][v] = jpegBuf;
                }
                if (isLLECData()) {
                    jpegBuf1 = data();
                    cell2(h, v, bs, jpegBuf1);
                }
            } else {
                if (isJPEGData()) {
                    len = mapFile.readInt2();
                    jpegBuf = new byte[len];
                    mapFile.readFully(jpegBuf);
                    jpegDatas[h][v] = jpegBuf;
                }
                if (isLLECData()) {
                    jpegBuf1 = data();
                    cell(h, v, bs, jpegBuf1);
                }
            }


        } catch (Exception e) {
            System.err.println("获取JPEG 数据块失败：" + e.getMessage());
        }
    }

    public byte[] getJpegData(int h, int v) {
        byte[] bs = (byte[]) jpegDatas[h][v];

        return bs;
    }

    /**
     * 获取指定的JPEG数据块（新地图使用）
     *
     * @param h 行
     * @param v 列
     * @return
     */
    public byte[] getJpegData2(int h, int v) {
        try {
            byte jpegBuf[] = null;
            ByteArrayOutputStream bos = new ByteArrayOutputStream(4096);
            boolean isFilled = false;// 是否0xFF->0xFF 0x00
            bos.reset();
            jpegBuf = (byte[]) jpegDatas[h][v];
            bos.write(jpegBuf, 0, 2);
            int p, start;
            isFilled = false;
            for (p = 4, start = 4; p < jpegBuf.length - 2; p++) {
                if (!isFilled && jpegBuf[p] == (byte) 0xFF && jpegBuf[++p] == (byte) 0xDA) {
                    isFilled = true;
                    jpegBuf[p + 2] = 12;
                    bos.write(jpegBuf, start, p + 10 - start);
                    bos.write(0);
                    bos.write(0x3F);
                    bos.write(0);
                    start = p + 10;
                    p += 9;
                }
                if (isFilled && jpegBuf[p] == (byte) 0xFF) {
                    bos.write(jpegBuf, start, p + 1 - start);
                    bos.write(0);
                    start = p + 1;
                }
            }
            bos.write(jpegBuf, start, jpegBuf.length - start);
            jpegDatas[h][v] = bos.toByteArray();
        } catch (Exception e) {
            System.err.println("获取JPEG 数据块失败：" + e.getMessage());
        }
        return (byte[]) jpegDatas[h][v];

    }

    private boolean isJPEGData2() {
        byte[] buf = new byte[4];
        try {
            int len = mapFile.read();
            mapFile.skipBytes(3 + len * 4);
            mapFile.read(buf);// 47 45 50 4A; GEPJ
            String str = new String(buf);
            return "GEPJ".equals(str);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return false;
    }


    /**
     * 清楚数据
     */
    public void removedata() {
//		jpegDatas=null;
        try {
            mapFile.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 获取数据
     */
    public byte[] data() {
        try {
            int e = this.mapFile.readInt2();
            Object jpegBuf = null;
            byte[] jpegBuf1 = new byte[e];
            this.mapFile.readFully(jpegBuf1);
            return jpegBuf1;
        } catch (IOException var3) {
            var3.printStackTrace();
            return null;
        }
    }

    private boolean isJPEGData() {
        byte[] buf = new byte[4];

        try {
            this.mapFile.read();
            this.mapFile.read(buf);
            String ex = new String(buf);
            return "GEPJ".equals(ex);
        } catch (IOException var3) {
            var3.printStackTrace();
            return false;
        }
    }

    private boolean isLLECData() {
        byte[] buf = new byte[4];

        try {
            this.mapFile.read(buf);
            String ex = new String(buf);
            return "LLEC".equals(ex);
        } catch (IOException var3) {
            var3.printStackTrace();
            return false;
        }
    }

    private boolean isValidMapFile() {
        byte[] buf = new byte[4];

        try {
            this.mapFile.read(buf);
            String ex = new String(buf);
            if ("HHYZ".equals(ex)) {
                this.isNew = false;
                return true;
            }

            if (ex.endsWith("ABCD")) {
                this.isNew = true;
                return true;
            }
        } catch (IOException var3) {
            var3.printStackTrace();
        }

        return false;
    }

    public String getFilename() {
        return filename;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    class MyRandomAccessFile extends RandomAccessFile {

        public MyRandomAccessFile(String name, String mode) throws FileNotFoundException {
            super(name, mode);
        }

        public MyRandomAccessFile(File file, String mode) throws FileNotFoundException {
            super(file, mode);
        }

        public int readInt2() throws IOException {
            int ch1 = this.read();
            int ch2 = this.read();
            int ch3 = this.read();
            int ch4 = this.read();
            if ((ch1 | ch2 | ch3 | ch4) < 0) {
                throw new EOFException();
            }
            return ((ch1 << 0) + (ch2 << 8) + (ch3 << 16) + (ch4 << 24));
        }
    }

    public int getHorSegmentCount() {
        return horSegmentCount;
    }

    public int getVerSegmentCount() {
        return verSegmentCount;
    }

}

